<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>课程设计(哈夫曼编译码器)-梁俊华</title>
</head>
<style>
    .title {
        width: auto;
        height: 50px;
        position: relative;
        text-align: center;
        font-size: 1.5em;
    }

    .title h {
        vertical-align: middle;
        line-height: 50px;
    }

    .page {
        width: auto;
        height: calc(100vh - 100px);
        position: relative;
        border: 1px solid #acacac;
    }

    .list {
        width: 150px;
        height: 100%;
        position: relative;
        text-align: center;
        border-right: 1px solid #acacac;
        overflow-y: scroll;
        overflow-x: hidden;
    }

    .list div {
        width: auto;
        height: 50px;
        border-bottom: 1px solid #acacac;
    }

    .list div h {
        vertical-align: middle;
        line-height: 50px;
    }

    .list div:hover {
        color: #3498db;
        background-color: rgb(238, 238, 238);
    }

    .list div:active {
        color: #3498db;
        background-color: #acacac;
    }

    .content {
        width: calc(100% - 190px);
        height: calc(100% - 40px);
        position: absolute;
        top: 0px;
        left: 150px;
        padding: 20px;
        font-size: 1em;
        overflow: scroll;
    }

    .content h {
        line-height: 2em;
    }

    .content p {
        line-height: 1.5em;
    }
</style>

<body>
    <div class="title">
        <h>帮助文档</h>
    </div>
    <div class="page">
        <div id="li" class="list">
            <div onclick="listClick(0)">
                <h>开发者</h>
            </div>
            <div onclick="listClick(1)">
                <h>简介</h>
            </div>
            <div onclick="listClick(2)">
                <h>数据结构</h>
            </div>
            <div onclick="listClick(3)">
                <h>哈夫曼编码</h>
            </div>
            <div onclick="listClick(4)">
                <h>哈夫曼译码</h>
            </div>
            <div onclick="listClick(5)">
                <h>编译码文件格式</h>
            </div>
            <div onclick="listClick(6)">
                <h>译文文件格式</h>
            </div>
            <div onclick="listClick(7)">
                <h>视频简介</h>
            </div>
            <div onclick="listClick(8)">
                <h>源代码</h>
            </div>
        </div>
        <div id="doc" class="content">
            <div>
                <h><b>开发者</b></h>
                <p>
                    姓名:梁俊华<br>
                    学院:计算机与通信学院<br>
                    专业:计算机科学与技术<br>
                    班级:2102班<br>
                </p>
            </div>
            <div>
                <h><b>简介</b></h>
                <p>
                    哈夫曼编码介绍：<br>
                    哈夫曼编码(Huffman
                    Coding)，又称霍夫曼编码，是一种编码方式，哈夫曼编码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法，该方法完全依据字符出现概率来构造异字头的平均长度最短的码字，有时称之为最佳编码，一般就叫做Huffman编码（有时也称为霍夫曼编码）。
                </p>
                <p>
                    哈夫曼编码\译码器介绍：<br>
                    哈夫曼编码\译码器,适用于window平台，是一款实现数据的哈夫曼编码和译码的windows桌面应用程序。<br>
                    配置属性：<br>
                    平台：Win32 <br>
                    字符集：Unicode字符集 <br>
                    编译方式：静态编译 <br>
                    功能支持：<br>
                    （1）输入文本进行哈夫曼编码。<br>
                    （2）读取ANSI编码的文本文件进行哈夫曼编码。<br>
                    （3）读取编码文件进行哈夫曼译码。<br>
                    （4）保存编码文件。<br>
                    （5）保存译码文件。<br>
                    （6）控制台查看程序运行情况。<br>
                    （7）程序帮助文档(html)。<br>
                    （8）单次编码最大容量30000字符。<br>
                </p>
            </div>
            <div>
                <h><b>数据结构</b></h>
                <p>
                    结构体：<br>
                    哈夫曼树信息结构：<br>
                    /*<br>
                    * 哈夫曼树结构体<br>
                    * TCHAR character：字符<br>
                    * UINT weightValue：权值<br>
                    * UINT parent：双亲结点<br>
                    * UINT leftChild：左孩子结点<br>
                    * UINT rightChild：右孩子结点<br>
                    * HFMTREE：哈夫曼树结构体<br>
                    * LPHFMTREE：哈夫曼树结构体指针<br>
                    */<br>
                    typedef struct SHFMTREE {<br>
                    TCHAR character;<br>
                    UINT weightValue;<br>
                    UINT parent;<br>
                    UINT leftChild;<br>
                    UINT rightChild;<br>
                    }HFMTREE, * LPHFMTREE;<br>
                    该语句定义了一个名为HFMTREE的结构体变量名，结构体用TCHAR类型定义了编码字符，UINT类型定义了字符权值和此结点的双亲、左孩子和右孩子。<br>
                </p>
                <p>
                    字符编码信息结构：<br>
                    /*<br>
                    *哈夫曼字符编码结构体<br>
                    * TCHAR character：字符<br>
                    * BOOL[] characterCode：字符编码<br>
                    * UINT characterCodeEffectiveLength：characterCode数组有效长度<br>
                    * HFMCHARACTERCODE：字符编码结构体<br>
                    * LPHFMCHARACTERCODE：字符编码结构体指针<br>
                    */<br>
                    typedef struct SHFMCHARACTERCODE {<br>
                    TCHAR character;<br>
                    BOOL characterCode[HFM_CODE_MAX_LEN];<br>
                    UINT characterCodeEffectiveLength;<br>
                    }HFMCHARACTERCODE, * LPHFMCHARACTERCODE;<br>
                    该语句定义了一个名为HFMCHARACTERCODE的结构体变量名，结构体用TCHAR类型定义了编码字符，用BOOL类型定义了字符编码，UINT类型定义了编码长度。<br>
                    <br>
                    字符集权值信息结构<br>
                    /*<br>
                    * 字符集权值结构体<br>
                    * TCHAR character：字符<br>
                    * UINT weightValue：权值<br>
                    * CHARACTERSETWEIGHT：字符集权值结构体<br>
                    * LPCHARACTERSETWEIGHT：字符集权值结构体指针<br>
                    */<br>
                    typedef struct SCHARACTERSETWEIGHT {<br>
                    TCHAR character;<br>
                    UINT weightValue;<br>
                    }CHARACTERSETWEIGHT, * LPCHARACTERSETWEIGHT;<br>
                    该语句定义了一个名为CHARACTERSETWEIGHT的结构体变量名，结构体用TCAHR定义了编码字符，用UINT定义了字符权值。<br>
                </p>
                <p>
                    结构体数组：<br>
                    <img src="结构体数组.bmp" alt="结构体数组图片"><br>
                </p>
                <p>
                    算法实现：<br>
                    <pre>
                        *
                        * 哈夫曼类
                        * 实现字符的哈夫曼编码和译码操作
                        */
                        class Hfm {
                        public:
                        typedef bool BOOL;
                        
                        //结构体定义
                        /*
                        * 哈夫曼树结构体
                        * TCHAR character：字符
                        * UINT weightValue：权值
                        * UINT parent：双亲结点
                        * UINT leftChild：左孩子结点
                        * UINT rightChild：右孩子结点
                        * HFMTREE：哈夫曼树结构体
                        * LPHFMTREE：哈夫曼树结构体指针
                        */
                        typedef struct SHFMTREE {
                        TCHAR character;
                        UINT weightValue;
                        UINT parent;
                        UINT leftChild;
                        UINT rightChild;
                        }HFMTREE, * LPHFMTREE;
                        
                        /*
                        *哈夫曼字符编码结构体
                        * TCHAR character：字符
                        * BOOL[] characterCode：字符编码
                        * UINT characterCodeEffectiveLength：characterCode数组有效长度
                        * HFMCHARACTERCODE：字符编码结构体
                        * LPHFMCHARACTERCODE：字符编码结构体指针
                        */
                        typedef struct SHFMCHARACTERCODE {
                        TCHAR character;
                        BOOL characterCode[HFM_CODE_MAX_LEN];
                        UINT characterCodeEffectiveLength;
                        }HFMCHARACTERCODE, * LPHFMCHARACTERCODE;
                        
                        /*
                        * 字符集权值结构体
                        * TCHAR character：字符
                        * UINT weightValue：权值
                        * CHARACTERSETWEIGHT：字符集权值结构体
                        * LPCHARACTERSETWEIGHT：字符集权值结构体指针
                        */
                        typedef struct SCHARACTERSETWEIGHT {
                        TCHAR character;
                        UINT weightValue;
                        }CHARACTERSETWEIGHT, * LPCHARACTERSETWEIGHT;
                        
                        //函数声明
                        /*
                        * 哈夫曼类析构函数
                        */
                        ~Hfm() noexcept;
                        
                        /*
                        * 哈夫曼类默认构造函数
                        */
                        Hfm() noexcept;
                        
                        /*
                        * 哈夫曼类构造函数
                        * LPTSTR const stringToBeEncoded：待编码字符串数组地址
                        * size_t stringToBeEncodedLength：stringToBeEncoded数组长度
                        */
                        Hfm(LPTSTR const stringToBeEncoded, size_t stringToBeEncodedLength) noexcept;
                        
                        /*
                        * 哈夫曼类构造函数
                        * LPHFMTREE const hfmTreeLength：哈夫曼树数组地址
                        * size_t hfmTreeLength：hfmTreeLength数组长度
                        * BOOL* const code：编码数组地址
                        * size_t codeLength：编码数组长度
                        */
                        Hfm(LPHFMTREE const hfmTree, size_t hfmTreeLength, BOOL* const code, size_t codeLength) noexcept;
                        
                        /*
                        * 哈夫曼类构造函数
                        * LPTSTR const stringToBeEncoded：待编码字符串数组地址
                        * size_t stringToBeEncodedLength：stringToBeEncoded数组长度
                        * characterSetWeight：字符集权值数组地址
                        * characterSetWeightLength：字符集权值数组长度
                        */
                        Hfm(LPTSTR const stringToBeEncoded, size_t stringToBeEncodedLength, LPCHARACTERSETWEIGHT const characterSetWeight,
                        size_t characterSetWeightLength) noexcept;
                        
                        /*
                        * 获取译码函数
                        * size_t* const decodingLength：译码数组长度
                        * 返回值：TCHAR const* 译码数组地址
                        * 异常抛出：throw(std::exception)
                        */
                        TCHAR const* GetDecoding(size_t* const decodingLength) throw(std::exception);
                        
                        /*
                        * 获取编码函数
                        * size_t* const codeLength：存储编码数组长度
                        * 返回值：Hfm::BOOL const* 编码数组地址
                        * 异常抛出：throw(std::exception)
                        */
                        BOOL const* GetCode(size_t* const codeLength)throw(std::exception);
                        
                        /*
                        * 获取哈夫曼树函数
                        * size_t* const hfmTreeLength：哈夫曼树数组长度
                        * 返回值：Hfm::HFMTREE const* 哈夫曼树数组地址
                        * 异常抛出：throw(std::exception)
                        */
                        HFMTREE const* GetHfmTree(size_t* const hfmTreeLength) throw(std::exception);
                        
                        /*
                        * 获取哈夫曼字符编码函数
                        * size_t* const hfmCharacterCodeLength：哈夫曼字符编码数组长度
                        * 返回值：Hfm::HFMCHARACTERCODE const* 哈夫曼字符编码数组地址
                        * 异常抛出：throw(std::exception)
                        */
                        HFMCHARACTERCODE const* GetHfmCharacterCode(size_t* const hfmCharacterCodeLength) throw(std::exception);
                        
                        /*
                        * 获取字符集权值函数
                        * size_t* const characterSetWeightLength：字符集权值数组长度
                        * 返回值：Hfm::CHARACTERSETWEIGHT const * 字符集权值数组地址
                        * 异常抛出：throw(std::exception)
                        */
                        CHARACTERSETWEIGHT const* GetCharacterSetWeight(size_t* const characterSetWeightLength) throw(std::exception);
                        
                        private:
                        //属性定义
                        INT structureMode = -1; //构造方式
                        BOOL* code = nullptr; //编码数组地址
                        LPTSTR decoding = nullptr; //译码数组地址
                        LPHFMTREE hfmTree = nullptr; //哈夫曼树数组地址
                        LPHFMCHARACTERCODE hfmCharacterCode = nullptr; //哈夫曼字符编码数组地址
                        LPCHARACTERSETWEIGHT characterSetWeight = nullptr; //字符集权值数组地址
                        size_t codeLength = 0; //编码数组长度
                        size_t decodingLength = 0; //译码数组长度
                        size_t hfmTreeLength = 0; //哈夫曼树数组长度
                        size_t hfmCharacterCodeLength = 0; //哈夫曼字符编码数组长度
                        size_t characterSetWeightLength = 0; //字符集权值数组长度
                        
                        //函数声明
                        size_t CreateHfmTree() throw(std::exception); //创建哈夫曼树函数
                        size_t HfmCharacterCode() throw(std::exception); //哈夫曼字符编码函数
                        size_t StatisticalCharacterSetWeight() throw(std::exception); //统计字符集权值函数
                        size_t HfmCode() throw(std::exception); //哈夫曼编码数函数
                        size_t HfmDecoding() throw(std::exception); //哈夫曼译码函数
                        };
                    </pre>
                </p>
            </div>
            <div>
                <h><b>编码</b></h>
                <p>
                    编码步骤：<br>
                    （1）获取字符集<br>
                    （2）获取字符集中字符的权值<br>
                    （3）建立哈夫曼树<br>
                    （4）字符集字符编码<br>
                    （5）数据编码<br>
                </p>
                <p>
                    实现函数：<br>
                    size_t CreateHfmTree() throw(std::exception);						//创建哈夫曼树函数 <br>
                    size_t HfmCharacterCode() throw(std::exception);					//哈夫曼字符编码函数<br>
                    size_t StatisticalCharacterSetWeight() throw(std::exception);		//统计字符集权值函数<br>
                    size_t HfmCode() throw(std::exception);								//哈夫曼编码数函数<br>
                    <pre>

                        /*
                        * 创建哈夫曼树函数
                        * 返回值：size_t 哈夫曼树数组长度
                        * 引用值：this->characterSetWeight和this->characterSetWeightLength
                        * 修改值：hfmTree和hfmTreeLength
                        * 异常抛出：throw(std::exception)
                        */
                        size_t Hfm::CreateHfmTree() throw(std::exception)
                        {
                            UINT	minIndex = 0, cminIndex = 0;				//最小值下标，次最小值下标					
                            UINT	min = UINT_MAX, cmin = UINT_MAX;			//最小值、次最小值
                            size_t	hfmTreeLength = 0;							//哈夫曼树数组长度
                            LPHFMTREE	hfmTree = nullptr;						//哈夫曼树数组地址
                            try {
                                //验证入参
                                if (this->characterSetWeight == nullptr) {
                                    throw std::runtime_error(HFM_INPUT_ERROR);
                                }
                                if (this->characterSetWeightLength > 0x7FFFFFFF) {
                                    throw std::runtime_error(HFM_MATHEMATICAL_OVERFLOW_OCCURS);
                                }
                                //建立哈夫曼树
                                hfmTreeLength = 2 * this->characterSetWeightLength - 1;
                                hfmTree = new HFMTREE[hfmTreeLength];
                                for (UINT i = 0; i < this->characterSetWeightLength; i++) {
                                    hfmTree[i].character = this->characterSetWeight[i].character;
                                    hfmTree[i].weightValue = this->characterSetWeight[i].weightValue;
                                    hfmTree[i].parent = hfmTree[i].leftChild = hfmTree[i].rightChild = UINT_MAX;
                                }
                                for (UINT i = this->characterSetWeightLength; i < hfmTreeLength; i++) {
                                    minIndex = 0, cminIndex = 0;
                                    min = UINT_MAX, cmin = UINT_MAX;
                                    for (UINT j = 0; j < i; j++) {
                                        if (hfmTree[j].parent == UINT_MAX) {
                                            if (hfmTree[j].weightValue < min) {
                                                cmin = min;
                                                cminIndex = minIndex;
                                                minIndex = j;
                                                min = hfmTree[j].weightValue;
                                            }
                                            else
                                                if (hfmTree[j].weightValue < cmin) {
                                                    cminIndex = j;
                                                    cmin = hfmTree[j].weightValue;
                                                }
                                        }
                                    }
                                    if (UINT_MAX - min < cmin) {
                                        throw std::runtime_error(HFM_MATHEMATICAL_OVERFLOW_OCCURS);
                                    }
                                    hfmTree[i].character = 0;
                                    hfmTree[i].weightValue = min + cmin;
                                    hfmTree[i].parent = UINT_MAX;
                                    hfmTree[i].leftChild = minIndex;
                                    hfmTree[i].rightChild = cminIndex;
                                    hfmTree[minIndex].parent = i;
                                    hfmTree[cminIndex].parent = i;
                                }
                            }
                            catch (std::exception err) {
                                //释放空间
                                delete[] hfmTree;
                                cout << "size_t Hfm::CreateHfmTree()" << endl;
                                throw err;
                            }
                            //修改属性值
                            this->hfmTree = hfmTree;
                            this->hfmTreeLength = hfmTreeLength;
                            return hfmTreeLength;
                        }
                        
                        /*
                        * 哈夫曼字符编码函数
                        * 返还值：size_t 哈夫曼编码数组长度
                        * 引用值：this->hfmTree和this->hfmTreeLength
                        * 修改值：hfmCharacterCode和hfmCharacterCodeLength
                        * 异常抛出：throw(std::exception)
                        */
                        size_t Hfm::HfmCharacterCode() throw(std::exception)
                        {
                            UINT	parentIndex = 0, targetIndex = 0;			//父节点下标、当前结点坐标
                            size_t	hfmCharacterCodeLength = 0;					//哈夫曼字符编码数组长度
                            LPHFMCHARACTERCODE	hfmCharacterCode = nullptr;		//哈夫曼字符编码数组地址
                            try {
                                //验证入参
                                if (this->hfmTree == nullptr) {
                                    throw std::runtime_error(HFM_INPUT_ERROR);
                                }
                                if (this->hfmTreeLength == SIZE_MAX) {
                                    throw std::runtime_error(HFM_MATHEMATICAL_OVERFLOW_OCCURS);
                                }
                                //字符编码
                                hfmCharacterCodeLength = (this->hfmTreeLength + 1) / 2;
                                if (hfmCharacterCodeLength <= 1) {
                                    throw std::runtime_error(HFM_SINGLE_CHARACTER_CANNOT_BE_ENCODED);
                                }
                                hfmCharacterCode = new HFMCHARACTERCODE[hfmCharacterCodeLength];
                                for (UINT i = 0; i < hfmCharacterCodeLength; i++) {
                                    UINT characterCodelength = 0;					//字符编码长度
                                    BOOL characterCode[HFM_CODE_MAX_LEN] = { 0 };	//字符编码
                                    targetIndex = i;
                                    parentIndex = this->hfmTree[targetIndex].parent;
                                    while (parentIndex != UINT_MAX) {
                                        if (this->hfmTree[parentIndex].leftChild == targetIndex) {
                                            characterCode[characterCodelength++] = false;
                                        }
                                        else {
                                            characterCode[characterCodelength++] = true;
                                        }
                                        targetIndex = parentIndex;
                                        parentIndex = this->hfmTree[targetIndex].parent;
                                    }
                                    for (UINT j = 0, k = characterCodelength - 1; j < characterCodelength; j++, k--) {
                                        hfmCharacterCode[i].characterCode[j] = characterCode[k];
                                    }
                                    hfmCharacterCode[i].character = this->hfmTree[i].character;
                                    hfmCharacterCode[i].characterCodeEffectiveLength = characterCodelength;
                                }
                            }
                            catch (std::exception err) {
                                //释放空间
                                delete[] hfmCharacterCode;
                                cout << "size_t Hfm::HfmCharacterCode()" << endl;
                                throw err;
                            }
                            //修改属性
                            this->hfmCharacterCode = hfmCharacterCode;
                            this->hfmCharacterCodeLength = hfmCharacterCodeLength;
                            return hfmCharacterCodeLength;
                        }
                        
                        /*
                        * 统计字符集权值函数
                        * 返回值：size_t 返回字符集权值数组长度
                        * 引用值：this->decodeing和this->decodeingLength
                        * 修改值：characterSetWeight和characterSetWeightLength
                        * 异常抛出：throw(std::exception)
                        */
                        size_t Hfm::StatisticalCharacterSetWeight() throw(std::exception)
                        {
                            UINT	decodingEffectiveLength = 0;							//译文数组有效长度
                            UINT	characterSetWeightIndex = 0;							//字符集权值数组下标
                            UINT	characterSetWeightLength = 0;							//字符集权值数组长度
                            LPCHARACTERSETWEIGHT	characterSetWeight = nullptr;			//字符集权值数组地址
                            struct SCHARACTERSET {											//字符集结构
                                TCHAR character;				//字符
                                UINT weightValue;				//权值
                                struct SCHARACTERSET* next;		//下一个结点地址
                            }*characterSet = nullptr, * lp1 = nullptr, * lp2 = nullptr;		//头节点、前一个结点、后一个结点
                            try {
                                //验证入参
                                if (this->decoding == nullptr) {
                                    throw std::runtime_error(HFM_INPUT_ERROR);
                                }
                                decodingEffectiveLength = _tcslen(this->decoding);
                                if (decodingEffectiveLength >= this->decodingLength) {
                                    throw std::runtime_error(HFM_NON_NULL_ENDING_OF_STRING);
                                }
                                //获取字符集并统计字符权值
                                for (UINT i = 0; i < decodingEffectiveLength; i++) {
                                    if (characterSet == nullptr) {
                                        characterSet = lp1 = new struct SCHARACTERSET;
                                        lp1->character = this->decoding[i];
                                        lp1->weightValue = 1;
                                        lp1->next = nullptr;
                                        characterSetWeightLength++;
                                    }
                                    else {
                                        lp2 = characterSet;
                                        while (lp2 != nullptr) {
                                            if (lp2->character == this->decoding[i]) {
                                                lp2->weightValue++;
                                                break;
                                            }
                                            lp2 = lp2->next;
                                        }
                                        if (lp2 == nullptr) {
                                            lp2 = new struct SCHARACTERSET;
                                            lp2->character = this->decoding[i];
                                            lp2->weightValue = 1;
                                            lp2->next = nullptr;
                                            lp1->next = lp2;
                                            lp1 = lp2;
                                            characterSetWeightLength++;
                                        }
                                    }
                                }
                                //保存数据到字符集权值数组
                                lp1 = lp2 = characterSet;
                                characterSetWeight = new CHARACTERSETWEIGHT[characterSetWeightLength];
                                while (lp1 != nullptr) {
                                    characterSetWeight[characterSetWeightIndex].character = lp1->character;
                                    characterSetWeight[characterSetWeightIndex].weightValue = lp1->weightValue;
                                    characterSetWeightIndex++;
                                    lp2 = lp1;
                                    lp1 = lp1->next;
                                    delete lp2;
                                }
                            }
                            catch (std::exception err) {
                                //释放空间
                                lp1 = lp2 = characterSet;
                                delete[] characterSetWeight;
                                while (lp1 != nullptr) {
                                    lp2 = lp1;
                                    lp1 = lp1->next;
                                    delete lp2;
                                }
                                cout << "size_t Hfm::StatisticalCharacterSetWeight()" << endl;
                                throw err;
                            }
                            //修改属性值
                            this->characterSetWeight = characterSetWeight;
                            this->characterSetWeightLength = characterSetWeightLength;
                            return characterSetWeightLength;
                        }
                        
                        /*
                        * 哈夫曼编码函数
                        * 返回值：size_t 返回编码数组长度
                        * 引用值：this->hfmCharacterCode、this->decoding、this->hfmCharacterCodeLength和this->decodingLength
                        * 修改值：code和codeLength
                        * 异常抛出：throw(std::exception)
                        */
                        size_t Hfm::HfmCode() throw(std::exception)
                        {
                            BOOL	matchingFlag = false;								//编码存在标志
                            BOOL* code = nullptr;										//编码数组地址
                            UINT	codeIndex = 0;										//编码数组下标
                            UINT	decodingEffectiveLength = 0;						//译码数组有效长度
                            size_t	codeLength = 0;										//编码数组长度
                            struct SHFMCODE {											//哈夫曼编码结构
                                UINT hfmCharacterCodeIndex;		//哈夫曼字符编码数组下标
                                SHFMCODE* next;					//下一个结点地址
                            }*hfmCode = nullptr, * lp1 = nullptr, * lp2 = nullptr;		//头节点、前一个结点、后一个结点
                            try {
                                //验证入参
                                if (this->hfmCharacterCode == nullptr || this->decoding == nullptr) {
                                    throw std::runtime_error(HFM_INPUT_ERROR);
                                }
                                decodingEffectiveLength = _tcslen(this->decoding);
                                if (decodingEffectiveLength >= this->decodingLength) {
                                    throw std::runtime_error(HFM_NON_NULL_ENDING_OF_STRING);
                                }
                                //计算编码所需的哈夫曼字符编码数组下标和编码长度
                                for (UINT i = 0; i < decodingEffectiveLength; i++) {
                                    matchingFlag = false;
                                    for (UINT j = 0; j < this->hfmCharacterCodeLength; j++) {
                                        if (this->decoding[i] == this->hfmCharacterCode[j].character) {
                                            if (hfmCode == nullptr) {
                                                hfmCode = lp1 = lp2 = new struct SHFMCODE;
                                                lp1->hfmCharacterCodeIndex = j;
                                                lp1->next = nullptr;
                                            }
                                            else {
                                                lp2 = new struct SHFMCODE;
                                                lp2->hfmCharacterCodeIndex = j;
                                                lp2->next = nullptr;
                                                lp1->next = lp2;
                                                lp1 = lp2;
                                            }
                                            codeLength += this->hfmCharacterCode[j].characterCodeEffectiveLength;
                                            matchingFlag = true;
                                            break;
                                        }
                                    }
                                    if (!matchingFlag) {
                                        throw std::runtime_error(HFM_ABNORMAL_MATCH_BETWEEN_CHARACTER_AND_CODE_TABLE);
                                    }
                                }
                                //编码
                                lp1 = lp2 = hfmCode;
                                code = new BOOL[codeLength];
                                while (lp1 != nullptr) {
                                    for (UINT i = 0; i < this->hfmCharacterCode[lp1->hfmCharacterCodeIndex].characterCodeEffectiveLength; i++) {
                                        code[codeIndex++] = this->hfmCharacterCode[lp1->hfmCharacterCodeIndex].characterCode[i];
                                    }
                                    lp2 = lp1;
                                    lp1 = lp1->next;
                                    delete lp2;
                                }
                            }
                            catch (std::exception err) {
                                //释放空间
                                lp1 = lp2 = hfmCode;
                                delete[] code;
                                while (lp1 != nullptr) {
                                    lp2 = lp1;
                                    lp1 = lp1->next;
                                    delete lp2;
                                }
                                cout << "size_t Hfm::HfmCode()" << endl;
                                throw err;
                            }
                            //修改属性
                            this->code = code;
                            this->codeLength = codeLength;
                            return codeLength;
                        }                        
                    </pre>
                </p>
            </div>
            <div>
                <h><b>译码</b></h>
                <p>
                    译码步骤：<br>
                    哈夫曼树和编码匹配获取译码<br>
                </p>
                <p>
                    实现函数：<br>
                    size_t HfmDecoding() throw(std::exception);							//哈夫曼译码函数<br>
                    <pre>
                        /*
                        * 哈夫曼译码函数
                        * 返回值：size_t 返回译码数组长度
                        * 引用值：this->hfmTree、this->code、this->hfmTreeLength和this->codeLength
                        * 修改值：decoding和decodingLength
                        * 异常抛出：throw(std::exception)
                        */
                        size_t Hfm::HfmDecoding() throw(std::exception)
                        {
                        UINT decodingIndex = 0; //译文数组下标
                        UINT hfmTreeTargetIndex = 0; //哈夫曼树数组当位置下标
                        size_t decodingLength = 0; //译码数组长度
                        LPTSTR decoding = nullptr; //译码数组地址
                        struct SDECODING { //哈夫曼译码结构
                        TCHAR character; //字符
                        SDECODING* next; //下一个结点
                        }*hfmDecoding = nullptr, * lp1 = nullptr, * lp2 = nullptr; //头结点、前一个结点、后一个结点
                        try {
                        //验证入参
                        if (this->hfmTree == nullptr || this->code == nullptr) {
                        throw std::runtime_error(HFM_INPUT_ERROR);
                        }
                        //译码
                        hfmTreeTargetIndex = this->hfmTreeLength - 1;
                        for (UINT i = 0; i < this->codeLength; i++) {
                            if (this->code[i] == false) {
                            hfmTreeTargetIndex = this->hfmTree[hfmTreeTargetIndex].leftChild;
                            }
                            else {
                            hfmTreeTargetIndex = this->hfmTree[hfmTreeTargetIndex].rightChild;
                            }
                            if (this->hfmTree[hfmTreeTargetIndex].leftChild == UINT_MAX || this->hfmTree[hfmTreeTargetIndex].rightChild ==
                            UINT_MAX) {
                            if (hfmDecoding == nullptr) {
                            hfmDecoding = lp1 = lp2 = new struct SDECODING;
                            lp1->character = this->hfmTree[hfmTreeTargetIndex].character;
                            lp1->next = nullptr;
                            }
                            else {
                            lp2 = new struct SDECODING;
                            lp2->character = this->hfmTree[hfmTreeTargetIndex].character;
                            lp2->next = nullptr;
                            lp1->next = lp2;
                            lp1 = lp2;
                            }
                            decodingLength++;
                            hfmTreeTargetIndex = this->hfmTreeLength - 1;
                            }
                            }
                            //保存数据到译码数组
                            decodingLength++;
                            lp1 = lp2 = hfmDecoding;
                            decoding = new TCHAR[decodingLength];
                            while (lp1 != nullptr) {
                            decoding[decodingIndex++] = lp1->character;
                            lp2 = lp1;
                            lp1 = lp1->next;
                            delete lp2;
                            }
                            decoding[decodingIndex] = 0;
                            }
                            catch (std::exception err) {
                            //释放空间
                            lp1 = lp2 = hfmDecoding;
                            delete[] decoding;
                            while (lp1 != nullptr) {
                            lp2 = lp1;
                            lp1 = lp1->next;
                            delete lp2;
                            }
                            cout << "size_t Hfm::HfmDecoding()" << endl; throw err; } //修改属性 this->decoding = decoding;
                                this->decodingLength = decodingLength;
                                return decodingLength;
                                }
                    </pre>
                </p>
            </div>
            <div>
                <h><b>编码文件格式</b></h>
                <p>
                    格式：<br>
                    （1）0-4字节，数据类型：size_t，含义：哈夫曼树长度。<br>
                    （2）4-8字节，数据类型：size_t，含义：编码长度<br>
                    （3）8-12字节，数据类型：UINT，含义：最后一个字节的数据量<br>
                    （4）数据<br>
                </p>
                <p>
                    数据部分按位编码。写入文件时由BOOL类型数据经过“|”操作获得。读取文件时会经过“&”操作恢复BOOL类型数据。<br>
                </p>
                <p>
                    图解：<br>
                    <img src="编码格式.bmp" alt="编码文件格式图解">
                </p>
            </div>
            <div>
                <h><b>译文文件格式</b></h>
                <p>
                    编码：ANSI编码<br>
                    实现函数：<br>
                    由微软提供WideCharToMultiByte()函数将Unicode格式文本转化转化为ANSI格式文本<br>
                </p>
            </div>
            <div>
                <h><b>视频简介</b></h>
                <p>
                    <video src="视频简介.mp4" width="100%" controls></video>
                </p>
            </div>
            <div>
                <h><b>源代码</b></h>
                <br>
                <a href="https://pan.baidu.com/s/1jzt8cqNGtq3nimpio4Y71w?pwd=js7j">百度网盘链接：提取码：js7j</a>
            </div>
        </div>
    </div>
    <script>
        let beforeElm;

        function listClick(index) {
            let elm = document.getElementById('li');
            let div = [];
            elm.childNodes.forEach(function (data) {
                if (data.nodeName == 'DIV') {
                    div.push(data);
                }
            });
            div[index].style.color = '#3498db';
            if (beforeElm) {
                beforeElm.style.color = '';
            }
            beforeElm = div[index];
            elm = document.getElementById('doc');
            div = [];
            elm.childNodes.forEach(function (data) {
                if (data.nodeName == 'DIV') {
                    div.push(data);
                }
            });
            div.forEach(function (data) {
                data.style.display = 'none';
            });
            div[index].style.display = 'inline';
        }
        listClick(0);
    </script>
</body>

</html>